课程卡片 vp-item 基础组件
vp-item 是知识付费小程序中最核心的基础组件,用于展示课程/内容的缩略图卡片。它需要支持多种类型(图文、视频)、遮罩层、标签、背景图片等特性。本节详细讲解组件的设计思路、Props 定义、样式实现以及小程序中的 z-index 注意事项。
组件设计分析
vp-item 需要满足以下场景:
| 场景 | 需要的能力 |
|---|---|
| 图文类型 | 展示背景图片 |
| 视频类型 | 展示播放按钮图标 |
| 付费内容 | 左上角"试读"/"未设置"标签 |
| 锁定内容 | 半透明遮罩 + 标题/副标题 |
| 激活标签 | 蓝色/灰色两种标签样式 |
Props 定义
interface VpItemProps {
// 遮罩相关
mask: boolean // 是否显示遮罩层
title: string // 遮罩上的主标题
subtitle: string // 遮罩上的副标题
// 标签相关
tag: string // 左上角标签文字
active: boolean // 标签是否激活(蓝色 vs 灰色)
// 背景相关
image: string // 背景图片 URL
// 尺寸
width: string | number // 卡片宽度(默认 168rpx)
height: string | number // 卡片高度(默认 300rpx)
// 类型
type: string // 素材类型(text/video),用于控制播放按钮
}
typescript
完整 Props 代码:
const props = defineProps({
mask: { type: Boolean, default: false },
title: { type: String, default: '' },
subtitle: { type: String, default: '' },
tag: { type: String, default: '' },
active: { type: Boolean, default: false },
image: { type: String, default: '' },
width: { type: [String, Number], default: '168rpx' },
height: { type: [String, Number], default: '300rpx' },
type: { type: String, default: 'text' }
})
typescript
组件结构
<template>
<view
class="relative rounded-3 overflow-hidden"
:style="{ width: width, height: height }"
>
<!-- 左上角标签 -->
<view
v-if="tag"
class="absolute left-0 top-0 z-50 px-2 py-1 text-sm rounded-br-3"
:class="activeClass"
>
{{ tag }}
</view>
<!-- 中间文字(遮罩时显示) -->
<view
v-if="title || subtitle"
class="absolute left-0 top-0 z-30 w-full h-full flex flex-col items-center justify-center"
>
<text class="font-bold text-white">{{ title }}</text>
<text class="text-sm text-white">{{ subtitle }}</text>
</view>
<!-- 遮罩层 -->
<view
v-if="mask"
class="absolute left-0 top-0 z-20 w-full h-full bg-black bg-opacity-20"
/>
<!-- 背景图片 -->
<view
class="absolute left-0 top-0 z-10 w-full h-full bg-cover bg-center"
:style="{ backgroundImage: `url(${image})` }"
/>
<!-- 插槽:播放按钮等 -->
<slot />
</view>
</template>
vue
标签样式(computed)
标签有激活(蓝色)和默认(灰色)两种状态:
import { computed } from 'vue'
const activeClass = computed(() => {
return props.active ? 'bg-blue-500 text-white' : 'bg-gray-400 text-white'
})
typescript
小程序 z-index 注意事项
在小程序中,z-index 的生效条件与 Web 不同:
Web 环境:z-index 只需要元素有 position: relative/absolute/fixed 即可生效。
小程序环境:参与 z-index 比较的所有兄弟元素都必须设置 position 属性。如果某个元素只有 z-index 而没有 position,其 z-index 不会生效。
解决方案:给所有需要 z-index 的元素都添加 absolute left-0 top-0:
<!-- 错误:只有 z-30 没有定位,z-index 不生效 -->
<view class="z-30 w-full h-full">
<!-- 正确:同时设置定位和 z-index -->
<view class="absolute left-0 top-0 z-30 w-full h-full">
vue
背景图片设置
使用 style 绑定设置背景图片:
<view
class="bg-cover bg-center"
:style="{ backgroundImage: `url(${props.image})` }"
/>
vue
注意:单引号嵌套问题。在小程序中使用 CSS 的 background-image: url(...) 时,URL 中的引号需要正确处理。
组件注册(easycom)
在 components 目录下创建 vp-item/vp-item.vue,easycom 会自动注册,无需手动 import:
<!-- 直接使用 -->
<vp-item
image="https://example.com/test.jpg"
tag="未设置"
:active="true"
title="标题"
subtitle="副标题"
:mask="true"
/>
vue
组件扩展点
通过 slot 支持中间区域的定制(如视频播放按钮):
<vp-item image="..." type="video">
<view v-if="type === 'video'" class="absolute z-40 ...">
<!-- 播放按钮 -->
</view>
</vp-item>
vue
vp-item 是后续所有列表页面的基础构建块,良好的 Props 设计和灵活的 slot 机制让它在各种场景下都能复用。
↑